function result=struct_edit2(cmd, arg, title)
% function to edit values in a structure
% Creates a 'modal' window to be used for editing. Only one such window per title can exist at a time.
%
% called with cmd = 'edit': makes new figure to open for editing the selected structure in arg1
% called with cmd = 'update': returns the modified entry
% called with cmd = 'dismiss': returns the unmofified entry
%
% this version is taken from struct_edit, but is general for structures.
% arg is the structure, and does not have all the "stuff" in the original
% struct edit. THis version specific for datac analysis, because structure is
% not as rigid.

%
% MODIFIED is a flag indicating whether any of the fields have been modified
% FIELDS is a structure:
% FIELDS.ind is an array of indices of valid (non-structure) fields in arg
% FIELDS.fmt is an array with an indicator for the format for each field

% Modified to WORK, 8/18/05 P. Manis, but with the STIM structure...
% for ACQ.

%-----------------------------------------------------------------------
persistent MODIFIED FIELDS TITLE
global localARG
result = [];

if(nargin < 3)
    title = '';
end;
if(nargin < 2)
    arg = localARG;
end;

switch(cmd)

    case 'edit'
        FIELDS=makeeditor(arg, 0, title);
        MODIFIED = 0;
        TITLE=title;

    case 'Redisplay'
        arg = get_local_data(TITLE);
        if(isempty(arg))
            QueMessage('Redisplay Failed...');
            return;
        end
        FORMATS = makeeditor(arg, 1, TITLE);
        MODIFIED=0;


    case 'dismiss'
        if(~MODIFIED)
            close
            return;
        end
        button = questdlg('Are you sure you want to discard any changes?',...
            'Continue Operation','Yes','No','Help','No');
        if strcmp(button,'Yes')
            close
            return
        end
        return;

    case 'EditDispatch' % do the edit operation...
        EditDispatch(TITLE, arg);

    otherwise
end

return;

%-----------------------------------------------------------------------
function result = get_local_data(win_title)
% get the data from userdata in the editor window
% note we call with just the window title.
global localARG
result = localARG;
return;


%-----------------------------------------------------------------------
function set_local_data(win_title, arg)
% set the data as userdata in the editor window so we can access it
% note non-symmetry with get_local_data - here we use the full dataset
global localARG
localARG = arg;


% -------------------------------------------------
% make the editor list -
%  read the names from the
% control stucture stored in userdata of the main window
% and build a list based on that
% As we do this the first time, we generate a parallel structure
% that holds the data types for each entry
%
% the list callback routes to a routine that handles the
% display window when we double-click
%
% 12/8/98 P. Manis

function [fields] = makeeditor(arg, redraw, title)

% get the length of the window we will need...
set_local_data(title, arg); % store locally
fields=[];
[fields, err] = get_valid_elements(arg);
if(err)
    return;
end;
[rows, cols] = winsize(fields);

scrsz = get(0,'ScreenSize'); % determine the position of the figure in the system window
boxht=0.5;  % 0.4 allows us about 20 lines in the basic display at our default res.
colwid=scrsz(3)/5;
scrb = scrsz(4)*((1-boxht)/2);
scrwd = cols*colwid;
scrl = (scrsz(3)-scrwd)/2;
scrht = (rows/14.5)*scrsz(4)*boxht;
scrl = 150; scrb = 50; scrl=scrwd=350; scrht = 400;
h0 = findobj('Tag', title); % check for pre-existing window
if(isempty(h0)) % if none, make one
    h0 = figure('Position', [scrl, scrb, scrwd, scrht], ...
        'Name', title, ...
        'NumberTitle', 'off', ...
        'Tag', title);
else
    if(nargin ==1)
        figure(h0); % select the existing one...
        return;
    else % second argument means redraw the window...
        figure(h0);
        clf;
    end
end

set(h0, 'Units', 'characters');
figsz=get(h0, 'Position');

b_ht = 1.7;
pad_check = 7; pad_edit = 5; pad_list = 5; pad_pop = 7;
pad_push = 4; pad_radio = 7; pad_static = 4;
but_vpad = 0.125;
but_hpad = 1.5;

but_vspc = b_ht + but_vpad; % space to allocate for each button or field

buttonlist = [];
%buttonlist = add_button(buttonlist, 'Update', 'struct_edit2 update', 'Update Information', 'Update', 'on');
%buttonlist = add_button(buttonlist, 'Update & Save', 'struct_edit2 updateandsave', 'Update and Save Information', 'Update and save', 'on');
%buttonlist = add_button(buttonlist, 'List', 'struct_edit2 listdata', 'List the current data', 'List the Data', 'on');
buttonlist = add_button(buttonlist, 'Redisplay', 'struct_edit2 Redisplay;', 'Redisplay the current data', 'Redisplay the Data', 'on');
buttonlist = add_button(buttonlist, 'Dismiss', 'struct_edit2 dismiss', 'Dismiss Record Changes', 'Dismiss', 'on');

maxtitle = 0;
for i=1:length(buttonlist)
    l = length(buttonlist(i).title);
    if(l > maxtitle) maxtitle = l;
    end
end
but_width = maxtitle+2*but_hpad;
button_x = but_hpad;  % place the buttons to the left side of the display
button_y = figsz(4)*0.75-1;

button_y = updateeditwindow(arg, fields, title); % do them ALL
set_local_data(title, arg); % store the data locally..... so we can access it on callback

cmd_vspc = but_vspc + 2*but_vpad; % extra padding
for i=1:length(buttonlist)
    b_y = button_y-i*cmd_vspc;
    if(~isempty(buttonlist(i).pos))
        b_y = 1;
    end
    h1=uicontrol('Parent', h0, ...
        'Units', 'characters', ...
        'FontUnits', 'points', ...
        'FontName', 'Arial', ...
        'FontSize', 9, ...
        'ForegroundColor', 'blue', ...
        'Position', [button_x b_y but_width+0.5 b_ht], ...
        'String', buttonlist(i).title, ...
        'ToolTipString', buttonlist(i).tooltip, ...
        'Callback', buttonlist(i).callback, ...
        'Enable', buttonlist(i).enable, ...
        'Tag', buttonlist(i).tag);
end

return;


function [rows, cols] = winsize(fields)
nfield=length(fields);
rows=20;
cols=ceil(nfield/rows);
return;

% -------------------------------------------------

function [fields, err] = get_valid_elements(arg)
% this function identifies the valid fields that can be displayed and edited.
% basically, only structures and cell arrays are not considered valid
% the return value fields is a structure:
% fields.ind are indices into the arg structure
% fields.name is the name of the arg.
% fields.fmt is the format (s = string, n = numeric) of the argument.
%
fields.ind=[];
fields.fmt=[];
fields.name=[];
fields.edit=[];
err = 0;
names=fieldnames(arg);
nfield = length(names);
start = 0;
k = 0;
in = strmatch('start', names); % look for start tag - is required...
if(isempty(in))
    fprintf(1, 'No ''start'' tag in structure - not editable\n');
    err = 1;
    return;
end;
for i=1:nfield
    thisname = char(names(i));
    switch(thisname)
        case 'start'
            start = 1;
        case 'end'
            start = 0;
        otherwise
            u=eval(['arg.' char(thisname)]); % try to evaluate it
            if(start) % if data is in a structure or cell array, it is excluded
                k = k + 1;
                fields(k).ind = i;
                fields(k).edit=1;
                fields(k).name = thisname;
                if(ischar(u)) % string
                    fields(k).fmt = 's'; % string
                else % numeric
                    fields(k).fmt = 'n'; % numeric
                end
                if(isstruct(u))
                    fn = fieldnames(u);
                    if(strmatch('f', fn))
           
                        if(findstr('%c', u.f) | findstr('%s', u.f))
                            fields(k).fmt = 's';
                        end;
                    end;
                end;
            end;
    end;
end



%---------------------------------------------------------------------------
% update the list in the edit window for the control structure
% 12/8/98 P. Manis

function [ymax] =  updateeditwindow(arg, fields, title)

h0=findobj('Tag', title);
if(isempty(h0))
    ymax = 0;
    return;
end;
figsz=get(h0, 'Position'); % in current units..
% figsz(3) is width in characters; figsz(4) is height in characters
[rows, cols] = winsize(fields);
field_vspc=1.8; % spacing in vertical direction
maxw=max(cellfun('length', {fields.name})); % find max width of all fields
field_hspc=maxw*2;

x1=1;
y1=figsz(4)-field_vspc;
y2=figsz(4)/5;
x2=1;

for i=1:length(fields)
    u=char(fields(i).name);
    if(fields(i).edit) % editable field, display it here...

        x=eval(char(['arg.' fields(i).name]));
        if(fields(i).fmt == 'n')
            v = sprintf([x.f ' '],x.v);
        else
            v = sprintf(x.f, x.v);
        end;
        l=(length(v)+4+cellfun('length', {fields(i).name})+5);
        xn=1.6*l+x1; % predict size of text on line
        if(xn > figsz(3)-12)
            x1 = 1; % restart next line
            xn=1.6*l+x1;
            y1=y1-field_vspc;
            h1 = text_entry(u, u, v, 0, x1, y1);
        else
            h1 = text_entry(u, u, v, 0, x1, y1);
            x1=xn;
        end;

    else % display the uneditable fields here
        u=char(fields(i).name);
        x=eval(char(['arg.' fields(i).name]));
        if(~iscell(x))
        if(fields(i).fmt == 'n')
            v = sprintf([x.f ' '],x.v);
        else
            v = sprintf(x.f, x.v);
        end;
            l=(length(v)+4+cellfun('length', {fields(i).name})+5);
            xn=1.6*l+x1; % predict size of text on line
            if(xn > figsz(3)-20)
                x1 = 1; % restart next line
                xn = 1.6*l+x1;
                y1=y1-field_vspc;
                h1 = text_entry(u, u, v, 0, x1, y1);
            else
                h1 = text_entry(u, u, v, 0, x1, y1);
            x1 = xn;
            end;

        end;

    end;
end;
ymax = y1;


%---------------------------------------------------------------------------
function EditDispatch(win_title, arg, fields)
% EditDispatch captures the input and updates the dataset whenever a field is edited.
%
h=findobj('Tag', arg); % get the displayed value
d = get_local_data(win_title); % get the current data set and find arg the valid elements of that set...
[fields, err] = get_valid_elements(d);
if(err)
    return;
end;
a=[];
for i=1:length(fields)
    cn = char(fields(i).name);
    a=strmatch(arg, cn);
    if(~isempty(a)) % that is the match - lets go.
        break;
    end
end
if(isempty(a)) % whoops, we did something wrong in the code
    disp(sprintf('Field %s not found in the dataset', arg))
    return;
end;
x=eval(['d.' cn]);
hs=get(h, 'String');
% generate a result according to the format statement in the field
% hs is ALWAYS a string...
% we have to strip the formatting to get it right.
if(findstr('n', fields(i).fmt ))
    fmt = '%g';
else
    fmt='%s';
end

c=sprintf('sscanf(hs, ''%s'')',fmt); % correctly format the display
s=eval(c);
eval(['d.' cn '.v=hs;']);
modified = 1;
disp('Update')
set_local_data(win_title, d);
return;


%---------------------------------------------------------------------------
% textentry.m - make a textbox with a label
% call: textbox(tag, label, initvalue, x, y)
% x and y are in character units...
function [handle] = text_entry(tag, label, initvalue, fieldwidth, x, y)

if(x < 0)
    style='text';
    x=-x;
else
    style='edit';
end;
b_ht = 1.4; % standard field height in characters
pad_static=4;
pad_edit=5;
fontsz=10;
fwtitle = length(label)+pad_static;
fwarg = fieldwidth+pad_edit - fwtitle;
if(fwarg < (length(initvalue)+pad_edit+1))
    fwarg = length(initvalue)+pad_edit+1;
end
h1 = uicontrol('Units','characters', ...
    'ListboxTop',0, ...
    'Position',[x y fwtitle b_ht], ...
    'HorizontalAlignment','left', ...
    'String',label, ...
    'Style','text', ...
    'FontSize', fontsz, ...
    'FontName', 'FixedWidth');
handle = uicontrol('Units','characters', ...
    'ListboxTop',0, ...
    'background', [1 1 1], ...
    'foreground', 'black', ...
    'Position',[x+fwtitle y fwarg b_ht], ...
    'HorizontalAlignment','right', ...
    'Max', 1, ...
    'Min', 0, ...
    'Style',style, ...
    'FontSize', fontsz, ...
    'FontName', 'FixedWidth', ...
    'UserData', label, ...
    'Tag',tag);
set(handle, 'String', initvalue);
set(handle, 'Callback', 'struct_edit2(''EditDispatch'', get(gcbo, ''UserData''))');













